
#include "BPDU.h"
#include "log.h"
#include "base_types.h"

// ============================================================================

const char* GetBpduPortRoleName (unsigned int bpduPortRole)
{
	if (bpduPortRole == 0)
		return "Unknown";
	else if (bpduPortRole == 1)
		return "Alt/Backup";
	else if (bpduPortRole == 2)
		return "Root";
	else if (bpduPortRole == 3)
		return "Designated";
	else
	{
		assert (false);
		return NULL;
	}
}

// ============================================================================

unsigned int GetBpduPortRole (PORT_ROLE role)
{
	if (role == PORT_ROLE_MASTER)
	{
		return BPDU_PORT_ROLE_MASTER;
	}
	else if ((role == PORT_ROLE_ALTERNATE) || (role == PORT_ROLE_BACKUP))
	{
		return BPDU_PORT_ROLE_ALT_BACKUP;
	}
	else if (role == PORT_ROLE_ROOT)
	{
		return BPDU_PORT_ROLE_ROOT;
	}
	else if (role == PORT_ROLE_DESIGNATED)
	{
		return BPDU_PORT_ROLE_DESIGNATED;
	}
	else
	{
		assert (false);
		return 0;
	}
}			

// ============================================================================

enum VALIDATED_BPDU_TYPE MSTP_GetValidatedBpduType (const unsigned char* _bpdu, int bpduSize)
{
	BPDU_HEADER* bpduHeader = (BPDU_HEADER*) _bpdu;
	if (bpduHeader->protocolId == 0)
	{
		// 14.4 in 802.1Q-2005
		if ((bpduHeader->bpduType == 0) && (bpduSize >= 35))
		{
			// a) - STP Configuration BPDU
			return VALIDATED_BPDU_TYPE_STP_CONFIG;
		}
		else if ((bpduHeader->bpduType == 0x80) && (bpduSize >= 4))
		{
			// b) - STP TCN BPDU
			return VALIDATED_BPDU_TYPE_STP_TCN;
		}
		else if ((bpduHeader->protocolVersionId == 2) && (bpduHeader->bpduType == 2) && (bpduSize >= 36))
		{
			// c) - RST BPDU
			return VALIDATED_BPDU_TYPE_RST;
		}
		else if ((bpduHeader->protocolVersionId >= 3) && (bpduHeader->bpduType == 2))
		{
			MSTP_BPDU* mstpBpdu = (MSTP_BPDU*) _bpdu;
		
			// Conditions d) / 1), 2), 3) don't make much sense or-ed, as they appear in the standard. Truncated
			// or malformed BPDUs would pass as valid RSTP BPDUs according to these conditions.
			// Let's just implement e) and discard non-matching BPDUs.
			if (bpduSize >= 102)
			{
				unsigned short version3Length = mstpBpdu->Version3Length.GetValue ();
				if ((mstpBpdu->Version1Length == 0) && ((version3Length % 16) == 0))
				{
					// MST BPDU
					return VALIDATED_BPDU_TYPE_MST;
				}
			}

			printf ("Truncated / malformed MSTP BPDU ignored.\r\n");
			return VALIDATED_BPDU_TYPE_UNKNOWN;
		}
	}
	
	return VALIDATED_BPDU_TYPE_UNKNOWN;
}

// ============================================================================

void DumpMstpBpdu (BRIDGE* bridge, int port, int tree, const MSTP_BPDU* bpdu, int indent)
{
	LOG (bridge, port, tree, "  Flags: TC={D}, Proposal={D}, PortRole={S}, Learning={D}, Forwarding={D}, Agreement={D}\r\n",
			bpdu->cistFlags.Tc,
			bpdu->cistFlags.Proposal,
			GetBpduPortRoleName (bpdu->cistFlags.PortRole),
			bpdu->cistFlags.Learning,
			bpdu->cistFlags.Forwarding,
			bpdu->cistFlags.Agreement);
	LOG (bridge, port, tree, "  CIST Root ID                 : {BID}\r\n", &bpdu->cistRootId);
	LOG (bridge, port, tree, "  CIST External Path Cost      : {D7}\r\n",   (int) bpdu->cistExternalPathCost.GetValue ());
	LOG (bridge, port, tree, "  CIST Regional Root ID        : {BID}\r\n", &bpdu->cistRegionalRootId);
	LOG (bridge, port, tree, "  CIST Internal Root Path Cost : {D7}\r\n",   (int) bpdu->cistInternalRootPathCost.GetValue ());
	LOG (bridge, port, tree, "  CIST Bridge ID               : {BID}\r\n", &bpdu->cistBridgeId);
	LOG (bridge, port, tree, "  CIST Port ID                 : {X4}\r\n",   (int) bpdu->cistPortId.GetValue ());
	LOG (bridge, port, tree, "  CIST MessageAge={D}, MaxAge={D}, HelloTime={D}, ForwardDelay={D}, remainingHops={D}\r\n",
		 bpdu->MessageAge.GetValue () / 256,
		 bpdu->MaxAge.GetValue () / 256,
		 bpdu->HelloTime.GetValue () / 256,
		 bpdu->ForwardDelay.GetValue () / 256,
		 (int) bpdu->cistRemainingHops);
}

// ============================================================================

void DumpRstpBpdu (BRIDGE* bridge, int port, int tree, const MSTP_BPDU* bpdu, int indent)
{
	LOG (bridge, port, tree, "  Flags: TC={D}, Proposal={D}, PortRole={S}, Learning={D}, Forwarding={D}, Agreement={D}\r\n",
			bpdu->cistFlags.Tc,
			bpdu->cistFlags.Proposal,
			GetBpduPortRoleName (bpdu->cistFlags.PortRole),
			bpdu->cistFlags.Learning,
			bpdu->cistFlags.Forwarding,
			bpdu->cistFlags.Agreement);
	LOG (bridge, port, tree, "  Root ID        : {BID}\r\n", &bpdu->cistRootId);
	LOG (bridge, port, tree, "  Root Path Cost : {D7}\r\n",   (int) bpdu->cistExternalPathCost.GetValue ());
	LOG (bridge, port, tree, "  Bridge ID      : {BID}\r\n", &bpdu->cistRegionalRootId);
	LOG (bridge, port, tree, "  Port ID        : {X4}\r\n",   (int) bpdu->cistPortId.GetValue ());
	LOG (bridge, port, tree, "  MessageAge={D}, MaxAge={D}, HelloTime={D}, ForwardDelay={D}\r\n",
		 bpdu->MessageAge.GetValue () / 256,
		 bpdu->MaxAge.GetValue () / 256,
		 bpdu->HelloTime.GetValue () / 256,
		 bpdu->ForwardDelay.GetValue () / 256);
}

// ============================================================================

void MST_CONFIG_ID::Dump (BRIDGE* bridge, int port, int tree) const
{
//	LOG (bridge, port, tree, "Name=\"%0.32s\", Digest=", ConfigurationName);
//	for (int i = 0; i < 16; i++)
//		mstpprintf ("%02x", ConfigurationDigest [i]);
	assert (false);
}

// ============================================================================
bool MST_CONFIG_ID::operator== (const MST_CONFIG_ID& rhs) const
{
	return Cmp (this, &rhs, sizeof (*this)) == 0;
}
